Load SCE and ACTIONet for a specific cell type

Choose from cell types: - Astrocytes: “Ast” - Excitatory Neurons: “Ex” - Inhibitory Neurons: “In” - Microglia: “Mic” - Oligodendrocytes: “Oli” - Oligodendrocyte Progenitor Cells: “OPC”

# Select cell type from list
cell.type = "Oli"
sce.file.name <- paste("/home/samsl/Pseudotemporal-GRNs/data/",cell.type,"/",cell.type,"_sce.rds",sep="")
ACTIONet.file.name <- paste("/home/samsl/Pseudotemporal-GRNs/data/",cell.type,"/",cell.type,"_ACTIONet.rds",sep="")
sce <- readRDS(sce.file.name)
ACTIONet.out <- readRDS(ACTIONet.file.name)

Identify Cell States Correlated with Disease

To produce a pseudotemporal ordering of cell states, we identify cell states which are correlated with disease. We can use braaksc, ceradsc, or cogdx as clinical indicators of disease progression. For now, we will use ceradsc. We plot enrichment for clinical phenotype across cell states to identify the maximally enriched cell state.

# Import Clinical Data from ROSMAP
clinical <- read.table("/home/samsl/Pseudotemporal-GRNs/data/Processed_10x_AD_data/ROSMAP_Clinical_2019-05_v3.csv", header = TRUE, sep=",")

cogdx.ind <- factor(unlist(lapply(colData(sce)$projid, function (x) { clinical[which(clinical$projid == x),"cogdx"] })))
braak.ind <- factor(unlist(lapply(colData(sce)$projid, function (x) { clinical[which(clinical$projid == x),"braaksc"] })))
cerad.ind <- factor(unlist(lapply(colData(sce)$projid, function (x) { clinical[which(clinical$projid == x),"ceradsc"] })))
indicators = list("braak" = braak.ind, "cogdx" = cogdx.ind, "cerad" = cerad.ind)

# Select Indicator to Use
selected.ind = "braak"

# Annotate ACTIONet with each clinical indicator
ACTIONet.out <- add.cell.annotations(ACTIONet.out, indicators[[selected.ind]], annotation.name=selected.ind)

# Visualize  the clinical indicators in the ACTIONet layout
plot.ACTIONet(ACTIONet.out, labels = ACTIONet.out$annotations[[selected.ind]]$Labels)

# Identify disease progression enriched archetypes
annot.out <- annotate.archetypes.using.labels(ACTIONet.out, indicators[[selected.ind]], core = TRUE)
png(paste("img/",cell.type,"_cell_state_",selected.ind,"_enrichment.png",sep=""))
Heatmap(t(annot.out$Enrichment), cluster_rows = FALSE, cluster_columns = FALSE, name='Enrichment', column_title='Cell State', column_title_side='bottom')

Biological Pathway Enrichment

We can verify the correlation of this cell state with progression by searching for enrichment with GO pathways.

data("nanoStringDB_human")
ADmarkers <- nanoStringDB_human$AD
enrichment.out <- t(geneset.enrichment.archetype(ACTIONet.out, ADmarkers))
png(paste0("img/",cell.type,"_cell_state_disease_enrichment.png"))
Heatmap(t(enrichment.out), cluster_columns=FALSE, name='Enrichment', column_title = 'Cell State', column_title_side = 'bottom')

Calculate Disease Score

This cell state can then be used to assign scores for each cell.

# Indicators vote for most correlated state
MAX.BRAAK.STAGE <- 6
braak.ind.enrich <- annotate.archetypes.using.labels(ACTIONet.out, indicators[["braak"]], core = TRUE)$Enrichment
braak.vote <- which.max(braak.ind.enrich[,MAX.BRAAK.STAGE])

MAX.CERAD.STAGE <- 1
cerad.ind.enrich <- annotate.archetypes.using.labels(ACTIONet.out, indicators[["cerad"]], core = TRUE)$Enrichment
cerad.vote <- which.max(cerad.ind.enrich[,MAX.CERAD.STAGE])

MAX.COGDX.STAGE <- 5
cogdx.ind.enrich <- annotate.archetypes.using.labels(ACTIONet.out, indicators[["cogdx"]], core = TRUE)$Enrichment
cogdx.vote <- which.max(cogdx.ind.enrich[,MAX.COGDX.STAGE])

DISEASE.ENRICH.COL <- 1
disease.enrich <- t(geneset.enrichment.archetype(ACTIONet.out, ADmarkers))
disease.vote <- which.max(disease.enrich[,DISEASE.ENRICH.COL])

ACTIVE.MIC.COL <- 2
active.mic.vote <-  which.max(disease.enrich[,ACTIVE.MIC.COL])


listMode <- function(v) {
   uniqv <- unique(v)
   uniqv[which.max(tabulate(match(v, uniqv)))]
}

correlated.state <- listMode(c(braak.vote, cerad.vote, cogdx.vote, disease.vote, active.mic.vote))

disease.score <- ACTIONet.out$unification.out$H.core[correlated.state,]
core.significance <- ACTIONet.out$unification.out$DE.core@assays[["significance"]]
associated.genes <- rownames(core.significance)[order(core.significance[,correlated.state], decreasing=TRUE)]
#png(paste("img/",cell.type,"_archetype_disease_score_gradient.png",sep=""))
plot.ACTIONet.gradient(ACTIONet.out,disease.score)

View Biological Pathways enriched in the correlated archetype

We can see which pathways are enriched in the correlated state and other states.

Labels from archetypes and progression

ACTIONet.out <- annotate.cells.from.archetype.enrichment(ACTIONet.out,t(annot.out$Enrichment),core = TRUE,annotation.name = paste0(cell.type,".enrichment.labels"))

png(paste0("/home/samsl/Pseudotemporal-GRNs/img/",cell.type,"_cell_labels_",selected.ind,"_enrichment_labels.png"))
plot.ACTIONet(ACTIONet.out, labels=paste0(cell.type,".enrichment.labels"))

Label cells based on disease score

plot.ACTIONet(ACTIONet.out, ACTIONet.out$unification.out$assignments.core)

plot.ACTIONet.3D(ACTIONet.out, ACTIONet.out$unification.out$assignments.core)
Loading required package: ggpubr
Loading required package: magrittr

Attaching package: ‘ggpubr’

The following object is masked from ‘package:scater’:

    mutate

Loading required package: threejs
nbins = 4

disc = discretize(as.data.frame(disease.score),"interval",breaks=nbins,ordered=TRUE)
disc$raw.score = disease.score

ggplot(disc, aes(x=raw.score)) + 
 geom_histogram(aes(y=..density..), colour="black", fill="white",binwidth = 1/nbins, boundary = 0, closed = "left")+
 geom_density(alpha=.2, fill="#FF6666") +
geom_rug(aes(y = 0), position = position_jitter(height = 0)) +
  coord_cartesian(xlim=c(0, 1))

#Cluster cells by disease score
M.genes = 100
top.genes = associated.genes[1:M.genes]
top.sce <- sce[rownames(sce) %in% top.genes]
df <- as.data.frame(as.matrix(t(counts(top.sce))))

K.centers = nbins
km <- kmeans(df, K.centers)
pc <- prcomp(t(df))
plot(pc$rotation[,1],disease.score, col=km$cluster, xlab="PC1")

plot(pc$rotation[,1],disease.score, col=d$disease.score, xlab="PC1")

autoplot(km, data=df)


clusts <- as.data.frame(list("CellNames" = colnames(sce),
                             "Cluster" = km$cluster,
                             "DiseaseScore" = disease.score,
                             "DiseaseRange" = disc$disease.score))

saveRDS(df,paste0("data/",cell.type,"/top_gene_counts.rds"))
saveRDS(clusts,paste0("data/",cell.type,"/clusters.rds"))

Monocle

Subset Cells

Get only the cells from the subcluster that you want, using the labels. Do this for each subcluster to get the layers.

ACTIONet.out <- cluster.ACTIONet.using.decomposition(ACTIONet.out, annotation.name="CellStateClusters")
plot.ACTIONet(ACTIONet.out, labels = ACTIONet.out$annotations$CellStateClusters$Labels, transparency.attr = ACTIONet.out$annotations$CellStateClusters$Labels.confidence)

noAD <- as.matrix(counts(sce[unlist(top.markers),which(ACTIONet.out$annotations$ceradsc$Labels == 4)]))
possibleAD <- as.matrix(counts(sce[unlist(top.markers),which(ACTIONet.out$annotations$ceradsc$Labels == 3)]))
probableAD <- as.matrix(counts(sce[unlist(top.markers),which(ACTIONet.out$annotations$ceradsc$Labels == 2)]))
definiteAD <- as.matrix(counts(sce[unlist(top.markers),which(ACTIONet.out$annotations$ceradsc$Labels == 1)]))

saveRDS(noAD, paste("data/noAD_",cell.type,".rds",sep=""))
saveRDS(possibleAD, paste("data/possibleAD_",cell.type,".rds",sep=""))
saveRDS(probableAD, paste("data/probableAD_",cell.type,".rds",sep=""))
saveRDS(definiteAD, paste("data/definiteAD_",cell.type,".rds",sep=""))
LS0tCnRpdGxlOiAiY29uc3RydWN0UHNldWRvdGVtcG9yYWxPcmRlcmluZy5SbWQiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSwgZWNobz1GQUxTRX0KcmVxdWlyZSgia25pdHIiKQpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIi9ob21lL3NhbXNsL1BzZXVkb3RlbXBvcmFsLUdSTnMiKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKYGBge3IgIkxvYWQgTGlicmFyaWVzIiwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CnJlcXVpcmUoQUNUSU9OZXQpCnJlcXVpcmUoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCnJlcXVpcmUoU2V1cmF0KQojcmVxdWlyZShtb25vY2xlMykKcmVxdWlyZShDb21wbGV4SGVhdG1hcCkKcmVxdWlyZShmYWN0b2V4dHJhKQpyZXF1aXJlKGdnZm9ydGlmeSkKcmVxdWlyZShibmxlYXJuKQpyZXF1aXJlKFVzaW5nUikKYGBgCgojIyMgTG9hZCBTQ0UgYW5kIEFDVElPTmV0IGZvciBhIHNwZWNpZmljIGNlbGwgdHlwZQpDaG9vc2UgZnJvbSBjZWxsIHR5cGVzOgogIC0gQXN0cm9jeXRlczogIkFzdCIKICAtIEV4Y2l0YXRvcnkgTmV1cm9uczogIkV4IgogIC0gSW5oaWJpdG9yeSBOZXVyb25zOiAiSW4iCiAgLSBNaWNyb2dsaWE6ICJNaWMiCiAgLSBPbGlnb2RlbmRyb2N5dGVzOiAiT2xpIgogIC0gT2xpZ29kZW5kcm9jeXRlIFByb2dlbml0b3IgQ2VsbHM6ICJPUEMiCmBgYHtyICJMb2FkIERhdGEifQojIFNlbGVjdCBjZWxsIHR5cGUgZnJvbSBsaXN0CmNlbGwudHlwZSA9ICJPbGkiCnNjZS5maWxlLm5hbWUgPC0gcGFzdGUoIi9ob21lL3NhbXNsL1BzZXVkb3RlbXBvcmFsLUdSTnMvZGF0YS8iLGNlbGwudHlwZSwiLyIsY2VsbC50eXBlLCJfc2NlLnJkcyIsc2VwPSIiKQpBQ1RJT05ldC5maWxlLm5hbWUgPC0gcGFzdGUoIi9ob21lL3NhbXNsL1BzZXVkb3RlbXBvcmFsLUdSTnMvZGF0YS8iLGNlbGwudHlwZSwiLyIsY2VsbC50eXBlLCJfQUNUSU9OZXQucmRzIixzZXA9IiIpCnNjZSA8LSByZWFkUkRTKHNjZS5maWxlLm5hbWUpCkFDVElPTmV0Lm91dCA8LSByZWFkUkRTKEFDVElPTmV0LmZpbGUubmFtZSkKYGBgCgojIElkZW50aWZ5IENlbGwgU3RhdGVzIENvcnJlbGF0ZWQgd2l0aCBEaXNlYXNlClRvIHByb2R1Y2UgYSBwc2V1ZG90ZW1wb3JhbCBvcmRlcmluZyBvZiBjZWxsIHN0YXRlcywgd2UgaWRlbnRpZnkgY2VsbCBzdGF0ZXMgd2hpY2ggYXJlIGNvcnJlbGF0ZWQgd2l0aCBkaXNlYXNlLiBXZSBjYW4gdXNlIGBicmFha3NjYCwgYGNlcmFkc2NgLCBvciBgY29nZHhgIGFzIGNsaW5pY2FsIGluZGljYXRvcnMgb2YgZGlzZWFzZSBwcm9ncmVzc2lvbi4gRm9yIG5vdywgd2Ugd2lsbCB1c2UgYGNlcmFkc2NgLiBXZSBwbG90IGVucmljaG1lbnQgZm9yIGNsaW5pY2FsIHBoZW5vdHlwZSBhY3Jvc3MgY2VsbCBzdGF0ZXMgdG8gaWRlbnRpZnkgdGhlIG1heGltYWxseSBlbnJpY2hlZCBjZWxsIHN0YXRlLgpgYGB7ciAiRmluZCBDbHVzdGVyIE1hcmtlcnMifQojIEltcG9ydCBDbGluaWNhbCBEYXRhIGZyb20gUk9TTUFQCmNsaW5pY2FsIDwtIHJlYWQudGFibGUoIi9ob21lL3NhbXNsL1BzZXVkb3RlbXBvcmFsLUdSTnMvZGF0YS9Qcm9jZXNzZWRfMTB4X0FEX2RhdGEvUk9TTUFQX0NsaW5pY2FsXzIwMTktMDVfdjMuY3N2IiwgaGVhZGVyID0gVFJVRSwgc2VwPSIsIikKCmNvZ2R4LmluZCA8LSBmYWN0b3IodW5saXN0KGxhcHBseShjb2xEYXRhKHNjZSkkcHJvamlkLCBmdW5jdGlvbiAoeCkgeyBjbGluaWNhbFt3aGljaChjbGluaWNhbCRwcm9qaWQgPT0geCksImNvZ2R4Il0gfSkpKQpicmFhay5pbmQgPC0gZmFjdG9yKHVubGlzdChsYXBwbHkoY29sRGF0YShzY2UpJHByb2ppZCwgZnVuY3Rpb24gKHgpIHsgY2xpbmljYWxbd2hpY2goY2xpbmljYWwkcHJvamlkID09IHgpLCJicmFha3NjIl0gfSkpKQpjZXJhZC5pbmQgPC0gZmFjdG9yKHVubGlzdChsYXBwbHkoY29sRGF0YShzY2UpJHByb2ppZCwgZnVuY3Rpb24gKHgpIHsgY2xpbmljYWxbd2hpY2goY2xpbmljYWwkcHJvamlkID09IHgpLCJjZXJhZHNjIl0gfSkpKQppbmRpY2F0b3JzID0gbGlzdCgiYnJhYWsiID0gYnJhYWsuaW5kLCAiY29nZHgiID0gY29nZHguaW5kLCAiY2VyYWQiID0gY2VyYWQuaW5kKQoKIyBTZWxlY3QgSW5kaWNhdG9yIHRvIFVzZQpzZWxlY3RlZC5pbmQgPSAiYnJhYWsiCgojIEFubm90YXRlIEFDVElPTmV0IHdpdGggZWFjaCBjbGluaWNhbCBpbmRpY2F0b3IKQUNUSU9OZXQub3V0IDwtIGFkZC5jZWxsLmFubm90YXRpb25zKEFDVElPTmV0Lm91dCwgaW5kaWNhdG9yc1tbc2VsZWN0ZWQuaW5kXV0sIGFubm90YXRpb24ubmFtZT1zZWxlY3RlZC5pbmQpCgojIFZpc3VhbGl6ZSAgdGhlIGNsaW5pY2FsIGluZGljYXRvcnMgaW4gdGhlIEFDVElPTmV0IGxheW91dApwbG90LkFDVElPTmV0KEFDVElPTmV0Lm91dCwgbGFiZWxzID0gQUNUSU9OZXQub3V0JGFubm90YXRpb25zW1tzZWxlY3RlZC5pbmRdXSRMYWJlbHMpCgojIElkZW50aWZ5IGRpc2Vhc2UgcHJvZ3Jlc3Npb24gZW5yaWNoZWQgYXJjaGV0eXBlcwphbm5vdC5vdXQgPC0gYW5ub3RhdGUuYXJjaGV0eXBlcy51c2luZy5sYWJlbHMoQUNUSU9OZXQub3V0LCBpbmRpY2F0b3JzW1tzZWxlY3RlZC5pbmRdXSwgY29yZSA9IFRSVUUpCnBuZyhwYXN0ZSgiaW1nLyIsY2VsbC50eXBlLCJfY2VsbF9zdGF0ZV8iLHNlbGVjdGVkLmluZCwiX2VucmljaG1lbnQucG5nIixzZXA9IiIpKQpIZWF0bWFwKHQoYW5ub3Qub3V0JEVucmljaG1lbnQpLCBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsIG5hbWU9J0VucmljaG1lbnQnLCBjb2x1bW5fdGl0bGU9J0NlbGwgU3RhdGUnLCBjb2x1bW5fdGl0bGVfc2lkZT0nYm90dG9tJykKYGBgCgojIEJpb2xvZ2ljYWwgUGF0aHdheSBFbnJpY2htZW50CldlIGNhbiB2ZXJpZnkgdGhlIGNvcnJlbGF0aW9uIG9mIHRoaXMgY2VsbCBzdGF0ZSB3aXRoIHByb2dyZXNzaW9uIGJ5IHNlYXJjaGluZyBmb3IgZW5yaWNobWVudCB3aXRoIEdPIHBhdGh3YXlzLgpgYGB7ciAiUGF0aHdheSBFbnJpY2htZW50In0KZGF0YSgibmFub1N0cmluZ0RCX2h1bWFuIikKQURtYXJrZXJzIDwtIG5hbm9TdHJpbmdEQl9odW1hbiRBRAplbnJpY2htZW50Lm91dCA8LSB0KGdlbmVzZXQuZW5yaWNobWVudC5hcmNoZXR5cGUoQUNUSU9OZXQub3V0LCBBRG1hcmtlcnMpKQpwbmcocGFzdGUwKCJpbWcvIixjZWxsLnR5cGUsIl9jZWxsX3N0YXRlX2Rpc2Vhc2VfZW5yaWNobWVudC5wbmciKSkKSGVhdG1hcCh0KGVucmljaG1lbnQub3V0KSwgY2x1c3Rlcl9jb2x1bW5zPUZBTFNFLCBuYW1lPSdFbnJpY2htZW50JywgY29sdW1uX3RpdGxlID0gJ0NlbGwgU3RhdGUnLCBjb2x1bW5fdGl0bGVfc2lkZSA9ICdib3R0b20nKQpgYGAKCiMgQ2FsY3VsYXRlIERpc2Vhc2UgU2NvcmUKVGhpcyBjZWxsIHN0YXRlIGNhbiB0aGVuIGJlIHVzZWQgdG8gYXNzaWduIHNjb3JlcyBmb3IgZWFjaCBjZWxsLgpgYGB7ciAiRGlzZWFzZSBTY29yZSJ9CiMgSW5kaWNhdG9ycyB2b3RlIGZvciBtb3N0IGNvcnJlbGF0ZWQgc3RhdGUKTUFYLkJSQUFLLlNUQUdFIDwtIDYKYnJhYWsuaW5kLmVucmljaCA8LSBhbm5vdGF0ZS5hcmNoZXR5cGVzLnVzaW5nLmxhYmVscyhBQ1RJT05ldC5vdXQsIGluZGljYXRvcnNbWyJicmFhayJdXSwgY29yZSA9IFRSVUUpJEVucmljaG1lbnQKYnJhYWsudm90ZSA8LSB3aGljaC5tYXgoYnJhYWsuaW5kLmVucmljaFssTUFYLkJSQUFLLlNUQUdFXSkKCk1BWC5DRVJBRC5TVEFHRSA8LSAxCmNlcmFkLmluZC5lbnJpY2ggPC0gYW5ub3RhdGUuYXJjaGV0eXBlcy51c2luZy5sYWJlbHMoQUNUSU9OZXQub3V0LCBpbmRpY2F0b3JzW1siY2VyYWQiXV0sIGNvcmUgPSBUUlVFKSRFbnJpY2htZW50CmNlcmFkLnZvdGUgPC0gd2hpY2gubWF4KGNlcmFkLmluZC5lbnJpY2hbLE1BWC5DRVJBRC5TVEFHRV0pCgpNQVguQ09HRFguU1RBR0UgPC0gNQpjb2dkeC5pbmQuZW5yaWNoIDwtIGFubm90YXRlLmFyY2hldHlwZXMudXNpbmcubGFiZWxzKEFDVElPTmV0Lm91dCwgaW5kaWNhdG9yc1tbImNvZ2R4Il1dLCBjb3JlID0gVFJVRSkkRW5yaWNobWVudApjb2dkeC52b3RlIDwtIHdoaWNoLm1heChjb2dkeC5pbmQuZW5yaWNoWyxNQVguQ09HRFguU1RBR0VdKQoKRElTRUFTRS5FTlJJQ0guQ09MIDwtIDEKZGlzZWFzZS5lbnJpY2ggPC0gdChnZW5lc2V0LmVucmljaG1lbnQuYXJjaGV0eXBlKEFDVElPTmV0Lm91dCwgQURtYXJrZXJzKSkKZGlzZWFzZS52b3RlIDwtIHdoaWNoLm1heChkaXNlYXNlLmVucmljaFssRElTRUFTRS5FTlJJQ0guQ09MXSkKCkFDVElWRS5NSUMuQ09MIDwtIDIKYWN0aXZlLm1pYy52b3RlIDwtICB3aGljaC5tYXgoZGlzZWFzZS5lbnJpY2hbLEFDVElWRS5NSUMuQ09MXSkKCgpsaXN0TW9kZSA8LSBmdW5jdGlvbih2KSB7CiAgIHVuaXF2IDwtIHVuaXF1ZSh2KQogICB1bmlxdlt3aGljaC5tYXgodGFidWxhdGUobWF0Y2godiwgdW5pcXYpKSldCn0KCmNvcnJlbGF0ZWQuc3RhdGUgPC0gbGlzdE1vZGUoYyhicmFhay52b3RlLCBjZXJhZC52b3RlLCBjb2dkeC52b3RlLCBkaXNlYXNlLnZvdGUsIGFjdGl2ZS5taWMudm90ZSkpCgpkaXNlYXNlLnNjb3JlIDwtIEFDVElPTmV0Lm91dCR1bmlmaWNhdGlvbi5vdXQkSC5jb3JlW2NvcnJlbGF0ZWQuc3RhdGUsXQpjb3JlLnNpZ25pZmljYW5jZSA8LSBBQ1RJT05ldC5vdXQkdW5pZmljYXRpb24ub3V0JERFLmNvcmVAYXNzYXlzW1sic2lnbmlmaWNhbmNlIl1dCmFzc29jaWF0ZWQuZ2VuZXMgPC0gcm93bmFtZXMoY29yZS5zaWduaWZpY2FuY2UpW29yZGVyKGNvcmUuc2lnbmlmaWNhbmNlWyxjb3JyZWxhdGVkLnN0YXRlXSwgZGVjcmVhc2luZz1UUlVFKV0KI3BuZyhwYXN0ZSgiaW1nLyIsY2VsbC50eXBlLCJfYXJjaGV0eXBlX2Rpc2Vhc2Vfc2NvcmVfZ3JhZGllbnQucG5nIixzZXA9IiIpKQpwbG90LkFDVElPTmV0LmdyYWRpZW50KEFDVElPTmV0Lm91dCxkaXNlYXNlLnNjb3JlKQpgYGAKCiMgVmlldyBCaW9sb2dpY2FsIFBhdGh3YXlzIGVucmljaGVkIGluIHRoZSBjb3JyZWxhdGVkIGFyY2hldHlwZQpXZSBjYW4gc2VlIHdoaWNoIHBhdGh3YXlzIGFyZSBlbnJpY2hlZCBpbiB0aGUgY29ycmVsYXRlZCBzdGF0ZSBhbmQgb3RoZXIgc3RhdGVzLgpgYGB7cn0KZGF0YSgiZ1Byb2ZpbGVyREJfaHVtYW4iKQpncEFEbWFya2VycyA8LSBnUHJvZmlsZXJEQl9odW1hbiRTWU1CT0wkYEdPOkJQYApncC5lbnJpY2htZW50Lm91dCA8LSBnZW5lc2V0LmVucmljaG1lbnQuYXJjaGV0eXBlKEFDVElPTmV0Lm91dCwgZ3BBRG1hcmtlcnMpCmdwLnRvcCA8LSBncC5lbnJpY2htZW50Lm91dFtvcmRlcihncC5lbnJpY2htZW50Lm91dFssY29ycmVsYXRlZC5zdGF0ZV0sIGRlY3JlYXNpbmcgPSBUUlVFKSxdWzE6MjAsXQpwbmcocGFzdGUwKCJpbWcvIixjZWxsLnR5cGUsIl9jZWxsX3N0YXRlX3BhdGh3YXlfZW5yaWNobWVudC5wbmciKSkKSGVhdG1hcChncC50b3AsIGNsdXN0ZXJfY29sdW1ucz1GQUxTRSwgbmFtZT0nRW5yaWNobWVudCcsIGNvbHVtbl90aXRsZT0nQ2VsbCBTdGF0ZScsIGNvbHVtbl90aXRsZV9zaWRlPSdib3R0b20nKQpgYGAKCiMgTGFiZWxzIGZyb20gYXJjaGV0eXBlcyBhbmQgcHJvZ3Jlc3Npb24KYGBge3IgIkxhYmVsIGNlbGxzIGJhc2VkIG9uIGVucmljaG1lbnQgZm9yIGRpc2Vhc2UgcHJvZ3Jlc3Npb24iIH0KQUNUSU9OZXQub3V0IDwtIGFubm90YXRlLmNlbGxzLmZyb20uYXJjaGV0eXBlLmVucmljaG1lbnQoQUNUSU9OZXQub3V0LHQoYW5ub3Qub3V0JEVucmljaG1lbnQpLGNvcmUgPSBUUlVFLGFubm90YXRpb24ubmFtZSA9IHBhc3RlMChjZWxsLnR5cGUsIi5lbnJpY2htZW50LmxhYmVscyIpKQoKcG5nKHBhc3RlMCgiL2hvbWUvc2Ftc2wvUHNldWRvdGVtcG9yYWwtR1JOcy9pbWcvIixjZWxsLnR5cGUsIl9jZWxsX2xhYmVsc18iLHNlbGVjdGVkLmluZCwiX2VucmljaG1lbnRfbGFiZWxzLnBuZyIpKQpwbG90LkFDVElPTmV0KEFDVElPTmV0Lm91dCwgbGFiZWxzPXBhc3RlMChjZWxsLnR5cGUsIi5lbnJpY2htZW50LmxhYmVscyIpKQpgYGAKCiMgTGFiZWwgY2VsbHMgYmFzZWQgb24gZGlzZWFzZSBzY29yZQpgYGB7cn0KcGxvdC5BQ1RJT05ldChBQ1RJT05ldC5vdXQsIEFDVElPTmV0Lm91dCR1bmlmaWNhdGlvbi5vdXQkYXNzaWdubWVudHMuY29yZSkKcGxvdC5BQ1RJT05ldC4zRChBQ1RJT05ldC5vdXQsIEFDVElPTmV0Lm91dCR1bmlmaWNhdGlvbi5vdXQkYXNzaWdubWVudHMuY29yZSkKYGBgCgpgYGB7cn0KbmJpbnMgPSA0CgpkaXNjID0gZGlzY3JldGl6ZShhcy5kYXRhLmZyYW1lKGRpc2Vhc2Uuc2NvcmUpLCJpbnRlcnZhbCIsYnJlYWtzPW5iaW5zLG9yZGVyZWQ9VFJVRSkKZGlzYyRyYXcuc2NvcmUgPSBkaXNlYXNlLnNjb3JlCgpnZ3Bsb3QoZGlzYywgYWVzKHg9cmF3LnNjb3JlKSkgKyAKIGdlb21faGlzdG9ncmFtKGFlcyh5PS4uZGVuc2l0eS4uKSwgY29sb3VyPSJibGFjayIsIGZpbGw9IndoaXRlIixiaW53aWR0aCA9IDEvbmJpbnMsIGJvdW5kYXJ5ID0gMCwgY2xvc2VkID0gImxlZnQiKSsKIGdlb21fZGVuc2l0eShhbHBoYT0uMiwgZmlsbD0iI0ZGNjY2NiIpICsKZ2VvbV9ydWcoYWVzKHkgPSAwKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIoaGVpZ2h0ID0gMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsIDEpKQpgYGAKCmBgYHtyfQojQ2x1c3RlciBjZWxscyBieSBkaXNlYXNlIHNjb3JlCk0uZ2VuZXMgPSAxMDAKdG9wLmdlbmVzID0gYXNzb2NpYXRlZC5nZW5lc1sxOk0uZ2VuZXNdCnRvcC5zY2UgPC0gc2NlW3Jvd25hbWVzKHNjZSkgJWluJSB0b3AuZ2VuZXNdCmRmIDwtIGFzLmRhdGEuZnJhbWUoYXMubWF0cml4KHQoY291bnRzKHRvcC5zY2UpKSkpCgpLLmNlbnRlcnMgPSBuYmlucwprbSA8LSBrbWVhbnMoZGYsIEsuY2VudGVycykKcGMgPC0gcHJjb21wKHQoZGYpKQpwbG90KHBjJHJvdGF0aW9uWywxXSxkaXNlYXNlLnNjb3JlLCBjb2w9a20kY2x1c3RlciwgeGxhYj0iUEMxIikKcGxvdChwYyRyb3RhdGlvblssMV0sZGlzZWFzZS5zY29yZSwgY29sPWQkZGlzZWFzZS5zY29yZSwgeGxhYj0iUEMxIikKYXV0b3Bsb3Qoa20sIGRhdGE9ZGYpCgpjbHVzdHMgPC0gYXMuZGF0YS5mcmFtZShsaXN0KCJDZWxsTmFtZXMiID0gY29sbmFtZXMoc2NlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2x1c3RlciIgPSBrbSRjbHVzdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEaXNlYXNlU2NvcmUiID0gZGlzZWFzZS5zY29yZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGlzZWFzZVJhbmdlIiA9IGRpc2MkZGlzZWFzZS5zY29yZSkpCgpzYXZlUkRTKGRmLHBhc3RlMCgiZGF0YS8iLGNlbGwudHlwZSwiL3RvcF9nZW5lX2NvdW50cy5yZHMiKSkKc2F2ZVJEUyhjbHVzdHMscGFzdGUwKCJkYXRhLyIsY2VsbC50eXBlLCIvY2x1c3RlcnMucmRzIikpCmBgYAoKIyBNb25vY2xlCmBgYHtyIE1vbm9jbGV9CmBgYAoKIyBTdWJzZXQgQ2VsbHMKR2V0IG9ubHkgdGhlIGNlbGxzIGZyb20gdGhlIHN1YmNsdXN0ZXIgdGhhdCB5b3Ugd2FudCwgdXNpbmcgdGhlIGxhYmVscy4gRG8gdGhpcyBmb3IgZWFjaCBzdWJjbHVzdGVyIHRvIGdldCB0aGUgbGF5ZXJzLgpgYGB7ciBTdWJzZXR9CkFDVElPTmV0Lm91dCA8LSBjbHVzdGVyLkFDVElPTmV0LnVzaW5nLmRlY29tcG9zaXRpb24oQUNUSU9OZXQub3V0LCBhbm5vdGF0aW9uLm5hbWU9IkNlbGxTdGF0ZUNsdXN0ZXJzIikKcGxvdC5BQ1RJT05ldChBQ1RJT05ldC5vdXQsIGxhYmVscyA9IEFDVElPTmV0Lm91dCRhbm5vdGF0aW9ucyRDZWxsU3RhdGVDbHVzdGVycyRMYWJlbHMsIHRyYW5zcGFyZW5jeS5hdHRyID0gQUNUSU9OZXQub3V0JGFubm90YXRpb25zJENlbGxTdGF0ZUNsdXN0ZXJzJExhYmVscy5jb25maWRlbmNlKQoKbm9BRCA8LSBhcy5tYXRyaXgoY291bnRzKHNjZVt1bmxpc3QodG9wLm1hcmtlcnMpLHdoaWNoKEFDVElPTmV0Lm91dCRhbm5vdGF0aW9ucyRjZXJhZHNjJExhYmVscyA9PSA0KV0pKQpwb3NzaWJsZUFEIDwtIGFzLm1hdHJpeChjb3VudHMoc2NlW3VubGlzdCh0b3AubWFya2Vycyksd2hpY2goQUNUSU9OZXQub3V0JGFubm90YXRpb25zJGNlcmFkc2MkTGFiZWxzID09IDMpXSkpCnByb2JhYmxlQUQgPC0gYXMubWF0cml4KGNvdW50cyhzY2VbdW5saXN0KHRvcC5tYXJrZXJzKSx3aGljaChBQ1RJT05ldC5vdXQkYW5ub3RhdGlvbnMkY2VyYWRzYyRMYWJlbHMgPT0gMildKSkKZGVmaW5pdGVBRCA8LSBhcy5tYXRyaXgoY291bnRzKHNjZVt1bmxpc3QodG9wLm1hcmtlcnMpLHdoaWNoKEFDVElPTmV0Lm91dCRhbm5vdGF0aW9ucyRjZXJhZHNjJExhYmVscyA9PSAxKV0pKQoKc2F2ZVJEUyhub0FELCBwYXN0ZSgiZGF0YS9ub0FEXyIsY2VsbC50eXBlLCIucmRzIixzZXA9IiIpKQpzYXZlUkRTKHBvc3NpYmxlQUQsIHBhc3RlKCJkYXRhL3Bvc3NpYmxlQURfIixjZWxsLnR5cGUsIi5yZHMiLHNlcD0iIikpCnNhdmVSRFMocHJvYmFibGVBRCwgcGFzdGUoImRhdGEvcHJvYmFibGVBRF8iLGNlbGwudHlwZSwiLnJkcyIsc2VwPSIiKSkKc2F2ZVJEUyhkZWZpbml0ZUFELCBwYXN0ZSgiZGF0YS9kZWZpbml0ZUFEXyIsY2VsbC50eXBlLCIucmRzIixzZXA9IiIpKQpgYGA=